#region Copyright (C) 2007 Jens Winter

//Copyright (C) 2007 Jens Winter

//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.

//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//Lesser General Public License for more details.

//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#endregion

using System;
using System.Collections.Generic;
using System.Data;
using Flynn.Core.ConnectionFactories;
using Flynn.Core.TaskResults;
using Flynn.Core.Tasks;
using MbUnit.Framework;
using Rhino.Mocks;

namespace Flynn.Core.Tests
{
    [TestFixture]
    public class EngineFixture
    {
        private MockRepository _mocks;

        private Engine _engine;
        private IProjectLoader _projectLoader;
        private ITaskExecutionService _taskExecutionService;

        [SetUp]
        public void SetUp()
        {
            _mocks = new MockRepository();

            _projectLoader = _mocks.CreateMock<IProjectLoader>();
            _taskExecutionService = _mocks.CreateMock<ITaskExecutionService>();

            _engine = new Engine(_projectLoader, _taskExecutionService);
        }

        [Test]
        public void Can_Create_Engine()
        {
            new Engine();
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ProjectLoader_Must_Not_Be_Null()
        {
            new Engine(null, _taskExecutionService);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TaskExecutionService_Must_Not_Be_Null()
        {
            new Engine(_projectLoader, null);
        }

        [Test]
        public void Can_Register_DatabaseType()
        {
            _engine.RegisterDatabase(typeof(FakeConnectionFactory), "FakeDatabaseType");
        }

        [Test]
        public void Can_Register_VersioningType()
        {
            _engine.RegisterVersioning(typeof(FakeVersioning), "FakeVersioning");
        }

        [Test]
        public void Can_Prepare_Database()
        {
            Project project = new Project();
            PreparingDatabaseResult expectedResult = new PreparingDatabaseResult();

            using (_mocks.Record())
            {
                Expect.Call(_projectLoader.LoadProject("MyProject.xml")).Return(project);
                Expect.Call(_taskExecutionService.ExecutePrepareDatabaseTask(project, "ConnectionString")).Return(expectedResult);
            }

            PreparingDatabaseResult actualResult;

            using (_mocks.Playback())
            {
                actualResult = _engine.PrepareDatabase("MyProject.xml", "ConnectionString");
            }

            Assert.AreSame(expectedResult, actualResult);
        }

        [Test]
        public void Preparing_Database_Fails_If_Loading_Project_Throws_Exception()
        {
            using (_mocks.Record())
            {
                Expect.Call(_projectLoader.LoadProject("MyProject.xml")).Throw(new LoadingProjectException("Failure message"));
            }

            using (_mocks.Playback())
            {
                PreparingDatabaseResult result = _engine.PrepareDatabase("MyProject.xml", "ConnectionString");

                Assert.IsTrue(result.Failed);
                Assert.AreEqual("Failure: Project couldn't be loaded (Failure message)", result.FailureMessage);
            }
        }

        [Test]
        public void Can_Update_Database()
        {
            Project project = new Project();
            UpdatingDatabaseResult expectedResult = new UpdatingDatabaseResult();

            using (_mocks.Record())
            {
                Expect.Call(_projectLoader.LoadProject("MyProject.xml")).Return(project);
                Expect.Call(_taskExecutionService.ExecuteUpdateDatabaseTask(project, "42", "ConnectionString")).Return(expectedResult);
            }

            UpdatingDatabaseResult actualResult;

            using (_mocks.Playback())
            {
                actualResult = _engine.UpdateDatabase("MyProject.xml", "42", "ConnectionString");
            }

            Assert.AreSame(expectedResult, actualResult);
        }

        [Test]
        public void Updating_Database_Fails_If_Loading_Project_Throws_Exception()
        {
            using (_mocks.Record())
            {
                Expect.Call(_projectLoader.LoadProject("MyProject.xml")).Throw(new LoadingProjectException("Failure message"));
            }

            using (_mocks.Playback())
            {
                UpdatingDatabaseResult result = _engine.UpdateDatabase("MyProject.xml", "42", "ConnectionString");

                Assert.IsTrue(result.Failed);
                Assert.AreEqual("Failure: Project couldn't be loaded (Failure message)", result.FailureMessage);
            }
        }

        [Test]
        public void Can_Check_Database()
        {
            Project project = new Project();
            CheckingDatabaseResult expectedResult = CheckingDatabaseResult.Success(string.Empty);

            using (_mocks.Record())
            {
                Expect.Call(_projectLoader.LoadProject("MyProject.xml")).Return(project);
                Expect.Call(_taskExecutionService.ExecuteCheckDatabaseTask(project, "ConnectionString")).Return(expectedResult);
            }

            CheckingDatabaseResult actualResult;

            using (_mocks.Playback())
            {
                actualResult = _engine.CheckDatabase("MyProject.xml", "ConnectionString");
            }

            Assert.AreSame(expectedResult, actualResult);
        }

        [Test]
        public void Checking_Database_Fails_If_Loading_Project_Throws_Exception()
        {
            using (_mocks.Record())
            {
                Expect.Call(_projectLoader.LoadProject("MyProject.xml")).Throw(new LoadingProjectException("Failure message"));
            }

            using (_mocks.Playback())
            {
                CheckingDatabaseResult result = _engine.CheckDatabase("MyProject.xml", "ConnectionString");

                Assert.IsTrue(result.Failed);
                Assert.AreEqual("Failure: Project couldn't be loaded (Failure message)", result.FailureMessage);
            }
        }
    }

    internal class FakeConnectionFactory : IConnectionFactory
    {
        public IDbConnection CreateConnection(string connectionString)
        {
            throw new NotImplementedException();
        }
    }

    internal class FakeVersioning : IVersioning
    {
        public string GetCurrentVersion(IDbConnection connection)
        {
            throw new NotImplementedException();
        }

        public void PrepareDatabase(IDbConnection connection)
        {
            throw new NotImplementedException();
        }

        public void SetCurrentVersion(string currentVersion, IDbConnection connection)
        {
            throw new NotImplementedException();
        }
    }
}